import { DownOutlined, SelectOutlined } from '@ant-design/icons';
import { App, Button, Checkbox, DatePicker, Dropdown, Form, Input, InputNumber, Layout, Modal, Select, Space, Switch, Table } from "antd";
import dayjs from 'dayjs';
import React, { useCallback, useEffect, useMemo, useReducer, useRef, useState } from "react";
import Attachments from '../components/Attachments';
import DisabledContext from '../components/disabledContext';
import FillTabs from "../components/FillTabs";
import Iconfont from '../components/Iconfont';
import InputNumberRange from '../components/InputNumberRange';
import RichTextField from '../components/RichTextField';
import SearchTable from '../components/SearchTable';
import SelectTable from '../components/SelectTable';
import UploadField from '../components/UploadField';
import useDrawer from "../components/useDrawer";
import useModal from "../components/useModal";
import alert from "../utils/alert";
import client from "../utils/client";
import styles from './mpPage.module.css';

const REF_SEPARATOR = '.';

/* eslint-disable no-eval */
export default function MetaPlatformPage({ metadata = {}, ...props }) {
  const { Main = {}, Children = [], Direction = 'V', Size = '' } = metadata;
  const [selected, setSelected] = useState(null); // 用来将主页面的选中行传递给子页面
  const mainRef = useRef(); // 用来将主页面的MPP传递给子页面

  const pageStyles = useMemo(() => {
    return {
      container: {
        display: 'flex',
        flexDirection: Direction === 'H' ? 'row' : 'column',
        gap: Direction === 'H' ? 8 : undefined,
      },
      main: {
        width: Direction === 'H' ? Size : undefined,
        height: Direction === 'V' ? Size : undefined,
      },
    };
  }, [Direction, Size]);

  const tabs = useMemo(() => Children.map(child => ({
    key: child.ID,
    closable: false,
    label: child.Title || child.Card,
    children: <CardPage metadata={child} mainSelected={selected} mainPage={mainRef} {...props}></CardPage>
  })), [Children, selected, props]);

  if (Children.length > 0) {
    return (
      <div className={styles.mppage} style={pageStyles.container}>
        <div style={pageStyles.main}>
          <CardPage metadata={Main} mainRef={mainRef} onPageSelect={setSelected} {...props}></CardPage>
        </div>
        <div>
          <FillTabs size="small" items={tabs}></FillTabs>
        </div>
      </div>
    );
  } else {
    return <CardPage metadata={Main} {...props}></CardPage>;
  }
}

function CardPage({ mainRef, metadata, mainSelected, mainPage, onPageSelect = () => null, ...props }) {
  const root = React.useRef();
  const MPP_inst = React.useRef();
  const [filter_form] = Form.useForm();
  const [edit_form] = Form.useForm();
  const [drawer, drawerHolder] = useDrawer();
  const [modal, modalHolder] = useModal();
  const { message, notification } = App.useApp();
  const rootWidth = root.current?.clientWidth;

  const { Card, Tables, Forms, Querys, Buttons, Event, Reference, Parent,
    OrderBy, Order, Orders, DialogWidth, SafeDelete, ShowFooter, DefaultPageSize = 20 } = metadata;

  // 静态访问对象
  const MPP = useMemo(() => {
    return new Proxy({}, {
      get(target, k) {
        return MPP_inst.current?.[k] ?? target[k];
      }
    });
  }, []);
  if (mainRef) mainRef.current = MPP;

  const buttons = useMemo(() => {
    return Buttons.map(b => ({
      ...b,
      visible: b.可见性 ? eval('(' + b.可见性 + ')') : () => true,
      available: b.可用性 ? eval('(' + b.可用性 + ')') : () => true
    }));
  }, [Buttons]);

  const readModels = useMemo(() => {
    const models = {};
    Tables.forEach(t => models[t.来源] = t.来源);
    Forms.forEach(f => models[f.来源] = f.来源);
    Querys.forEach(q => models[q.来源] = q.来源);
    if (Reference) models[Reference] = Reference;
    if (Parent) models[Parent] = Parent;
    if (OrderBy) models[OrderBy] = OrderBy;
    if (Orders && Orders.length) Orders.forEach(o => models[o.By] = o.By);
    return Object.keys(models);
  }, [Tables, Forms, Querys, Reference, Parent, OrderBy, Orders]);

  const writeModels = useMemo(() => {
    const models = {};
    Forms.forEach(f => models[f.来源] = f.来源);
    if (Reference) models[Reference] = Reference;
    if (Parent) models[Parent] = Parent;
    return Object.keys(models);
  }, [Forms, Reference, Parent]);

  const events = useMemo(() => {
    const target = {};
    Object.keys(Event).forEach(k => {
      if (!Event[k]) return;
      target[k] = eval('(' + Event[k] + ')');
    });
    return target;
  }, [Event]);

  const tableColumns = useMemo(() => {
    const columns = Tables.filter(t => t.宽度 > 0).map((t, i) => Object.assign({
      title: t.名称,
      dataIndex: t.来源,
      width: t.宽度,
      align: t.对齐,
      fixed: t.冻结,
      ellipsis: true,
      sorter: {
        multiple: i + 1,
      },
      render(text) {
        if (text == null) return text;
        switch (t.类型) {
          case 'SZ': return Number(text).toFixed(t.精度 || 0);
          case 'RQ': return dayjs(text).format('YYYY-MM-DD');
          case 'SJ': return dayjs(text).format('YYYY-MM-DD HH:mm:ss');
          case 'BJ': return <Checkbox disabled checked={text} />;
          default: return text;
        }
      },
    }, t.渲染函数 ? {
      render: eval('(' + t.渲染函数 + ')'),
    } : null));

    const btn_names = buttons.filter(b => b.位置 === 'operate').map(b => b.名称);
    if (btn_names.length === 0) return columns;

    return [...columns, {
      key: '操作',
      title: '操作',
      width: btn_names.join('').length * 15 + (btn_names.length) * 8 + 16, // 一个字算15px, 按钮间距8px, td留白左右共16px
      className: styles.operator,
      render(text, record, index) {
        return (
          <Space>
            {
              buttons.filter(b => b.位置 === 'operate').filter(btn => btn.visible.call(MPP, record)).map(btn => (
                <Button key={btn.ID} type="link" danger={btn.危险} disabled={!btn.available.call(MPP, record)}
                  onClick={e => {
                    e.stopPropagation();
                    MPP.click(btn.名称, { record });
                  }}>{btn.名称}</Button>
              ))
            }
          </Space>
        );
      },
    }];
    // eslint-disable-next-line
  }, [MPP, Tables, buttons, mainSelected]);

  const queryComponents = useMemo(() => {
    const getComponent = (q, args) => {
      if (q.匹配方式 === 'NV') return <QueryCheckbox {...args} />;
      // 解析指定的组件
      switch (q.组件) {
        case 'dropdown': return <MetaSelect allowClear mode={q.匹配方式 === 'IN' ? 'multiple' : null} {...args} meta={q} />;
        case 'popup': return <MetaPopup {...args} meta={q} />;
        case 'text': return <Input allowClear autoComplete="off" placeholder={q.匹配方式 === 'LK' ? '*' : ''} {...args} />;
        case 'number': return q.匹配方式 === 'BT' ? <InputNumberRange {...args} /> : <InputNumber {...args} />;
        case 'date': return q.匹配方式 === 'BT' ? <DatePicker.RangePicker showTime={false} allowClear autoComplete="off" {...args} /> : <DatePicker showTime={false} allowClear autoComplete="off" {...args} />;
        case 'time': return q.匹配方式 === 'BT' ? <DatePicker.RangePicker showTime allowClear autoComplete="off" {...args} /> : <DatePicker showTime allowClear autoComplete="off" {...args} />;
        case 'checkbox': return <QueryCheckbox {...args} />;
        case 'switch': return <Switch {...args} />;
        default: break;
      }

      // 根据类型识别组件
      switch (q.类型) {
        case 'SZ': return q.匹配方式 === 'BT' ? <InputNumberRange /> : <InputNumber />;
        case 'RQ': return q.匹配方式 === 'BT' ? <DatePicker.RangePicker showTime={false} allowClear autoComplete="off" /> : <DatePicker showTime={false} allowClear autoComplete="off" />;
        case 'SJ': return q.匹配方式 === 'BT' ? <DatePicker.RangePicker showTime allowClear autoComplete="off" /> : <DatePicker showTime allowClear autoComplete="off" />;
        case 'BJ': return <QueryCheckbox />;
        case 'WB':
        case 'LS':
        default: return <Input allowClear autoComplete="off" placeholder={q.匹配方式 === 'LK' ? '*' : ''} />;
      }
    };

    return Querys.map((q, i) => {
      const { initialValue, ...args } = q.组件参数 ? eval('(' + q.组件参数 + ')') : {};
      const component = getComponent(q, args);
      return (
        <Form.Item key={i} label={q.名称} name={q.来源} className={'span-' + (q.匹配方式 === 'BT' ? 2 : 1)}
          valuePropName={component.type === QueryCheckbox || component.type === Switch ? 'checked' : 'value'}
          initialValue={initialValue}
        >
          {component}
        </Form.Item>
      );
    });
  }, [Querys]);

  const formComponents = useMemo(() => {
    const endlines = [];
    const getComponents = (f, args) => {
      // 解析指定的组件
      if (f.只读) args.disabled = true;
      switch (f.组件) {
        case 'dropdown': return <MetaSelect allowClear placeholder={f.提示} {...args} meta={f} />;
        case 'popup': return <MetaPopup placeholder={f.提示} {...args} meta={f} />;
        case 'text': return <Input allowClear autoComplete="off" placeholder={f.提示} prefix={f.前缀} suffix={f.后缀} {...args} />;
        case 'textarea': return <Input.TextArea autoComplete="off" placeholder={f.提示} {...args} />;
        case 'richtext': return <RichTextField placeholder={f.提示} {...args} />;
        case 'number': return <InputNumber placeholder={f.提示} precision={f.精度} prefix={f.前缀} addonAfter={f.后缀} {...args} />;
        case 'date': return <DatePicker showTime={false} allowClear placeholder={f.提示} {...args} />;
        case 'time': return <DatePicker showTime allowClear placeholder={f.提示} {...args} />;
        case 'file': return <UploadField {...args} />;
        case 'checkbox': return <Checkbox title={f.提示} {...args}>{f.提示}</Checkbox>;
        case 'switch': return <Switch {...args} title={f.提示} />;
        default: break;
      }

      // 根据类型识别组件
      if (f.只读 || f.来源.includes(REF_SEPARATOR)) args.disabled = true;
      switch (f.类型) {
        case 'SZ': return <InputNumber title={f.提示} placeholder={f.提示} precision={f.精度} prefix={f.前缀} addonAfter={f.后缀} {...args} />;
        case 'RQ': return <DatePicker showTime={false} allowClear placeholder={f.提示} {...args} />;
        case 'SJ': return <DatePicker showTime allowClear placeholder={f.提示} {...args} />;
        case 'BJ': return <Checkbox title={f.提示} {...args}>{f.提示}</Checkbox>;
        case 'WB':
        case 'LS':
        default: return <Input allowClear autoComplete="off" prefix={f.前缀} suffix={f.后缀} placeholder={f.提示} {...args} />;
      }
    };

    const items = Forms.map((f, i) => {
      if (f.换行) endlines.push(i);
      const { initialValue, ...args } = f.组件参数 ? eval('(' + f.组件参数 + ')') : {};
      const bool_props = (f.类型 === 'BJ' && !f.组件) || f.组件 === 'checkbox' || f.组件 === 'switch' ? {
        valuePropName: 'checked',
        initialValue: initialValue || false,
      } : {};
      return (
        <Form.Item key={i} label={f.名称} name={f.来源} className={'span-' + f.跨度}
          rules={f.必填 && f.跨度 > 0 ? [{ required: true }] : []} initialValue={initialValue} {...bool_props}>
          {getComponents(f, args)}
        </Form.Item>
      );
    });

    // 处理换行
    let i = endlines.pop();
    while (i !== undefined) {
      items.splice(i + 1, 0, <div key={'endline-' + i} className={styles.endline}></div>);
      i = endlines.pop();
    }
    return items;
  }, [Forms]);

  const visibleQueryComponents = useMemo(() => {
    if (!rootWidth) return queryComponents;
    let blocks = Math.max(1, Math.floor((rootWidth - 116) / 280));
    let count = 0;
    Querys.some(q => {
      blocks -= (q.匹配方式 === 'BT' ? 2 : 1);
      return blocks < 0 ? true : (count++ && false);
    });
    return queryComponents.slice(0, count);
  }, [Querys, queryComponents, rootWidth]);

  const onAdvanceQuery = useCallback(() => {
    const _drawer = drawer.open({
      title: '高级查询',
      placement: "left",
      content: (
        <Form form={filter_form} className={styles['super-filter']}>
          {queryComponents}
        </Form>
      ),
      extra: (
        <Space>
          <Button onClick={() => filter_form.resetFields()}>重置</Button>
          <Button type='primary' onClick={() => {
            _drawer.destroy();
            const values = filter_form.getFieldsValue();
            dispatch({ type: 'change', filters: values });
          }}>查询</Button>
        </Space>
      ),
    });
  }, [filter_form, drawer, queryComponents]);

  const withWorkflow = useMemo(() => {
    return writeModels.includes('流程实例');
  }, [writeModels]);

  const [state, dispatch] = useReducer((state, action) => {
    switch (action.type) {
      case 'loading':
        return { ...state, loading: true };
      case 'loaded':
        return {
          ...state,
          selected: null,  // 重新加载必须清空当前选中行，因为可能与加载后的新纪录不一致
          checkedRows: [], // 重新加载必须清空当前选中行，因为可能与加载后的新纪录不一致
          loading: false,
          data: action.data,
          pagination: {
            ...state.pagination,
            total: action.total,
            current: Math.min(Math.floor(action.total / state.pagination.pageSize) + 1, state.pagination.current),
          },
        };
      case 'overwrite':
        const record = state.data.find(d => d.ID === action.record.ID);
        Object.assign(record, action.record);
        return {
          ...state,
          data: [...state.data],
        };
      case 'change':
        return {
          ...state,
          filters: { ...state.filters, ...action.filters },
          pagination: Object.assign(state.pagination, action.pagination),
          sorter: action.sorter || state.sorter,
        };
      case 'check':
        return { ...state, checkedRows: action.rows };
      case 'select':
        return { ...state, selected: action.record === state.selected ? null : action.record };
      default:
        return { ...state, ...action };
    }
  }, {
    channel: withWorkflow ? '待办' : '查询',
    loading: true,
    filters: null,
    sorter: [],
    pagination: {
      showTotal: total => `共${total}条`,
      showSizeChanger: true,
      total: 0,
      pageSize: DefaultPageSize,
      current: 1,
    },
    data: [],
    selected: null,
    checkedRows: [],
  });

  MPP_inst.current = {
    ...client,
    message, modal, notification,
    props, state, dispatch,
    alert,
    reload: (id) => id ? loadRecord(id) : loadData(),
    click: (name, args) => onButtonClick(name, args),
    main: mainSelected,
    parent: mainPage?.current,
  };

  const tableSummary = useMemo(() => {
    if (ShowFooter) {
      const values = Tables.filter(t => t.宽度 > 0).map(t => {
        if (t.类型 !== 'SZ' || t.渲染函数) return '';
        let total = 0;
        state.data.forEach((record) => total += record[t.来源]);
        return total.toFixed(t.精度 || 0);
      });
      return (
        <Table.Summary fixed>
          <Table.Summary.Row>
            <Table.Summary.Cell index={0}></Table.Summary.Cell>
            {values.map((v, i) => <Table.Summary.Cell key={i + 1} index={i + 1}>{v}</Table.Summary.Cell>)}
            <Table.Summary.Cell index={values.length + 1}></Table.Summary.Cell>
          </Table.Summary.Row>
        </Table.Summary>
      );
    } else {
      return null;
    }
  }, [Tables, ShowFooter, state.data]);

  const getQueryFilter = useCallback(() => {
    const filters = [];
    if (Reference) filters.push([Reference, '=', mainSelected ? mainSelected.ID : null]);
    Object.keys(state.filters).forEach(key => {
      let value = state.filters[key];
      if (null == value || '' === value) return;
      if (Array.isArray(value) && value.length === 0) return;
      const { 匹配方式, 组件, 类型 } = Querys.find(q => q.来源 === key) || {};
      let op;
      switch (匹配方式) {
        case 'BT': {
          // 日期范围查询时，开始时间要设置到00:00:00，截止时间要设置到23:59:59
          if (组件 === 'date' || 类型 === 'RQ') {
            value[0] = value[0].startOf('day');
            value[1] = value[1].endOf('day');
          }
          op = Array.isArray(value) ? 'between' : '=';
          break;
        }
        case 'IN': {
          op = Array.isArray(value) ? 'in' : '=';
          break;
        }
        case 'LK': {
          op = 'like';
          break;
        }
        case 'NV': {
          op = 'is';
          value = value ? !null : null;
          break;
        }
        case 'EQ':
        default: {
          op = '=';
          break;
        }
      }
      filters.push([key, op, value]);
    });
    return filters;
  }, [Reference, Querys, mainSelected, state.filters]);

  const getQueryOrder = useCallback(() => {
    const order = {};
    const sorter = (Array.isArray(state.sorter) ? state.sorter : [state.sorter]).filter(s => s.order);
    sorter.forEach(s => order[s.field] = s.order);
    // 增加默认排序
    if (sorter.length === 0) {
      if (OrderBy) {
        // 元数据指定了默认排序字段
        order[OrderBy] = Order || 'asc';
      } else if (Orders) {
        Orders.forEach(o => order[o.By] = o.Order || 'asc');
      } else {
        // 动态取第一个列, 升序
        order[readModels[0]] = 'asc';
      }
    }
    return order;
  }, [Order, OrderBy, Orders, readModels, state.sorter]);

  const loadRecord = useCallback(async (id) => {
    const args = {
      Card,
      Model: readModels,
      Filter: [['ID', '=', id]],
    };
    let { Rows: [record] } = events['card.Load'] ? await events['card.Load'].call(MPP, args) : await client.job('CardR', args);
    dispatch({ type: 'overwrite', record });
  }, [Card, MPP, events, readModels]);

  const loadData = useCallback(async () => {
    dispatch({ type: 'loading' });

    const args = {
      Card,
      Model: readModels,
      Filter: getQueryFilter(),
      Order: getQueryOrder(),
      PageSize: state.pagination.pageSize,
      PageNo: state.pagination.current,
      Parent,
    };

    if (withWorkflow) {
      if (!client.has(Card + '.id')) {
        const [cardName, packageCode] = Card.split('.').reverse();
        const { Rows: [c] } = await client.job('CardR', {
          Card: '卡片',
          Model: ['名称', '开发包.代码'],
          Filter: [['名称', '=', cardName], ['开发包.代码', '=', packageCode]],
        });
        client.set(Card + '.id', c.ID);
      }
      if (state.channel === '待办') {
        // 待办数量一般不会太多, 直接全部加载
        const { Rows } = await client.job('CardR', {
          Card: 'B.流程待办',
          Model: ['流程实例', '流程实例.流程定义.数据卡片', '待办用户'],
          Filter: [['待办用户', '=', client.get('user').ID], ['流程实例.流程定义.数据卡片', '=', client.get(Card + '.id')]],
        });
        args.Filter.push(['流程实例', 'in', Rows.map(r => r.流程实例)]);
      }
      if (state.channel === '已办') {
        // 已办数量会很多, 临时限定为最近100条
        const { Rows } = await client.job('CardR', {
          Card: 'B.流程记录',
          Model: ['流程实例', '流程实例.流程定义.数据卡片', '创建用户', '创建时间', '回滚标记'],
          Filter: [['回滚标记', '=', false], ['创建用户', '=', client.get('user').ID], ['流程实例.流程定义.数据卡片', '=', client.get(Card + '.id')]],
          Order: { '创建时间': 'desc' },
          PageSize: 100,
          PageNo: 1,
        });
        args.Filter.push(['流程实例', 'in', Rows.map(r => r.流程实例)]);
      }
    }
    if (events['card.beforeLoad']) {
      const result = await events['card.beforeLoad'].call(MPP, args);
      if (result === false) {
        dispatch({ type: 'loaded', total: 0, data: [] });
        return;
      }
    }

    if (Parent && args.Filter.length === 0) args.Filter.push([Parent, 'is', null]);
    let { Total, Rows } = events['card.Load'] ? await events['card.Load'].call(MPP, args) : await client.job(Parent ? 'TreeR' : 'CardR', args);
    if (withWorkflow) Rows.forEach(r => r.channel = state.channel);

    if (events['card.afterLoad']) {
      await events['card.afterLoad'].call(MPP, Rows);
    }

    dispatch({ type: 'loaded', total: Total, data: Rows });
  }, [MPP, Card, Parent, events, getQueryFilter, getQueryOrder, readModels, withWorkflow, state.channel, state.pagination]);

  const onInfo = useCallback((record) => {
    edit_form.setFieldsValue(record);

    // 打开弹窗
    modal.open({
      title: '记录详情',
      width: DialogWidth || '90%',
      content: (
        <DisabledContext.Provider value={true}>
          <Form form={edit_form} className={styles['edit-form']} disabled>
            {formComponents}
          </Form>
        </DisabledContext.Provider>
      ),
    });
  }, [DialogWidth, edit_form, formComponents, modal]);

  const onEdit = useCallback(async (record) => {
    const title = record ? '修改记录' : '新增记录';
    const other = {};
    if (record) {
      const editing = { ...record };
      if (events['data.beforeEdit']) {
        const result = await events['data.beforeEdit'].call(MPP, editing);
        if (result === false) return;
      }
      edit_form.setFieldsValue(editing);
    } else {
      if (Reference) {
        if (mainSelected) {
          other[Reference] = mainSelected.ID;
        } else {
          alert('无效操作', '请先选择主表记录', { type: 'error' });
          return;
        }
      }
      // 树型结构要自动设置好上级引用的值
      if (Parent && state.selected) {
        other[Parent] = state.selected.ID;
      }
      edit_form.resetFields();
      record = {};
      if (events['data.beforeInsert']) {
        const result = await events['data.beforeInsert'].call(MPP, record);
        if (result === false) return;
      }
      edit_form.setFieldsValue(record);
    }

    const save = async () => {
      const values = await edit_form.validateFields();
      const target = Object.assign({}, record, values, other);

      if (events['data.beforeSave']) {
        const result = await events['data.beforeSave'].call(MPP, target, handle);
        if (result === false) return;
      }

      // 空字符串值转换为 null
      Object.keys(target).forEach(k => {
        target[k] = target[k] === '' ? null : target[k];
      });

      const [{ record: written, create_mode }] = await client.job(withWorkflow ? 'B.Flow_Create' : 'CardW', {
        Card,
        Model: writeModels,
        Record: target,
      });

      if (events['data.afterSave']) await events['data.afterSave'].call(MPP, written, create_mode);
      return written;
    };

    const commit = async () => {
      const written = await save();
      await client.job('B.Flow_Action', {
        InstanceID: written.流程实例,
        NodeID: written['流程实例.当前节点'],
        Action: '提交',
      });
    };

    // 打开弹窗
    const handle = modal.open({
      title,
      width: DialogWidth || '90%',
      maskClosable: false,
      content: (
        <Form form={edit_form} className={styles['edit-form']}
          onValuesChange={(changed, all) => {
            if (events['data.onChange']) events['data.onChange'].call(MPP, changed, all, edit_form);
          }}
        >
          {formComponents}
        </Form>
      ),
      footer: (
        <>
          {buttons.filter(b => b.位置 === 'form').map(btn => (
            <Button key={btn.ID} type={btn.类型} danger={btn.危险} hidden={!btn.visible.call(MPP, record)} disabled={!btn.available.call(MPP, record)}
              onClick={() => {
                if (events['button.onClick']) {
                  events['button.onClick'].call(MPP, btn.名称, { record, form: edit_form });
                }
              }}>{btn.名称}</Button>
          ))}
          <Button onClick={() => handle.destroy()}>取消</Button>
          <Button type={withWorkflow ? 'default' : 'primary'} onClick={() => save().then(() => {
            handle.destroy();
            MPP.reload();
          })}>保存</Button>
          {withWorkflow ? <Button type="primary" onClick={() => commit().then(() => {
            handle.destroy();
            MPP.reload();
          })}>提交</Button> : null}
        </>
      ),
    });
  }, [MPP, Card, DialogWidth, Reference, Parent, state.selected, buttons, edit_form, events, formComponents, mainSelected, modal, writeModels, withWorkflow]);

  const onRemove = useCallback((record, checkedRows) => {
    Modal.confirm({
      title: '删除确认',
      content: "数据删除后不可恢复, 请确定",
      wrapClassName: 'mp-dialog',
      async onOk() {
        const children = {};
        if (metadata.children) {
          metadata.children.forEach(c => children[c.Card] = c.Reference)
        }

        const args = {
          Card,
          Records: checkedRows?.length ? checkedRows : [record],
          Parent,
          Children: metadata.children ? children : undefined,
          Safe: SafeDelete ? true : false,
        };

        if (events['data.beforeDelete']) {
          const result = await events['data.beforeDelete'].call(MPP, args);
          if (result === false) return;
        }

        await client.job(withWorkflow ? 'B.Flow_Delete' : (Parent ? 'TreeD' : 'CardD'), args);

        dispatch({ type: 'select' });
        if (events['data.afterDelete']) await events['data.afterDelete'].call(MPP, record);
        MPP.reload();
      }
    });
  }, [MPP, Card, Parent, SafeDelete, withWorkflow, events, metadata.children]);

  const onAttachment = useCallback((record) => {
    modal.open({
      title: '附件管理',
      width: 650,
      bodyStyle: { height: 'calc(100vh - 180px)' },
      content: <Attachments card={Card} id={record.ID} />,
    });
  }, [modal, Card]);

  const xlsExport = useCallback(async () => {
    const args = {
      Card,
      Model: readModels,
      Filter: getQueryFilter(),
      Order: getQueryOrder(),
      Parent,
    };

    if (withWorkflow) {
      if (!client.has(Card + '.id')) {
        const [cardName, packageCode] = Card.split('.').reverse();
        const { Rows: [c] } = await client.job('CardR', {
          Card: '卡片',
          Model: ['名称', '开发包.代码'],
          Filter: [['名称', '=', cardName], ['开发包.代码', '=', packageCode]],
        });
        client.set(Card + '.id', c.ID);
      }
      if (state.channel === '待办') {
        // 待办数量一般不会太多, 直接全部加载
        const { Rows } = await client.job('CardR', {
          Card: 'B.流程待办',
          Model: ['流程实例', '流程实例.流程定义.数据卡片', '待办用户'],
          Filter: [['待办用户', '=', client.get('user').ID], ['流程实例.流程定义.数据卡片', '=', client.get(Card + '.id')]],
        });
        args.Filter.push(['流程实例', 'in', Rows.map(r => r.流程实例)]);
      }
      if (state.channel === '已办') {
        // 已办数量会很多, 临时限定为最近100条
        const { Rows } = await client.job('CardR', {
          Card: 'B.流程记录',
          Model: ['流程实例', '流程实例.流程定义.数据卡片', '创建用户', '创建时间', '回滚标记'],
          Filter: [['回滚标记', '=', false], ['创建用户', '=', client.get('user').ID], ['流程实例.流程定义.数据卡片', '=', client.get(Card + '.id')]],
          Order: { '创建时间': 'desc' },
          PageSize: 100,
          PageNo: 1,
        });
        args.Filter.push(['流程实例', 'in', Rows.map(r => r.流程实例)]);
      }
    }
    if (events['card.beforeLoad']) {
      const result = await events['card.beforeLoad'].call(MPP, args);
      if (result === false) return;
    }

    if (Parent && args.Filter.length === 0) args.Filter.push([Parent, 'is', null]);

    client.export_xls(Parent ? 'B.TreeRExport' : 'B.CardRExport', args);
  }, [MPP, Card, Parent, events, readModels, getQueryFilter, getQueryOrder, state.channel, withWorkflow]);

  const xlsImport = useCallback(() => {
    client.import('B.CardWImport', {
      Card,
      Model: writeModels,
    }).then(loadData);
  }, [Card, writeModels, loadData]);

  const onButtonClick = useCallback(async (name, { record, checkedRows }) => {
    if (events['button.onClick']) {
      const result = await events['button.onClick'].call(MPP, name, { record, checkedRows });
      if (result === false) return;
    }

    switch (name) {
      case '详情':
        onInfo(record);
        break;
      case '新增':
        onEdit();
        break;
      case '修改':
        onEdit(record);
        break;
      case '删除':
        onRemove(record, checkedRows);
        break;
      case '附件':
        onAttachment(record);
        break;
      case '导入':
        xlsImport();
        break;
      case '导出':
        xlsExport();
        break;
      default: break;
    }
  }, [MPP, events, onEdit, onInfo, onRemove, onAttachment, xlsExport, xlsImport]);

  useEffect(() => {
    if (state.filters !== null) loadData();
    // eslint-disable-next-line
  }, [mainSelected, state.filters, state.sorter, state.pagination.current, state.pagination.pageSize]);

  useEffect(() => {
    onPageSelect(state.selected);
  }, [onPageSelect, state.selected]);

  useEffect(() => {
    if (events['card.ready']) events['card.ready'].call(MPP, metadata);
    filter_form.submit();
    // eslint-disable-next-line
  }, []);

  function MetaSelect({ meta, onChange, Filter, Order, onLoad, ...selectProps }) {
    const form = Form.useFormInstance();
    const is_in_filter = form === filter_form;

    const [state, dispatch] = useReducer((state, action) => {
      return { ...state, ...action }
    }, {
      rows: [],
      options: selectProps.options,
      open: false,
    });

    const refer_name = meta.来源.split(REF_SEPARATOR).shift();
    const refer_fields = is_in_filter ? [...Querys, ...Forms, ...Tables].filter(q => q.来源.startsWith(refer_name + REF_SEPARATOR))
      : Forms.filter(f => f.来源.startsWith(refer_name + REF_SEPARATOR));

    const loadModel = Array.from(new Set(refer_fields.map(f => f.来源.replace(refer_name + REF_SEPARATOR, ''))));

    const onLoad_default = async () => {
      if (!meta.目标卡片) return;

      return await client.job('CardR', {
        Card: meta.目标卡片,
        Model: loadModel,
        Filter: typeof Filter === 'function' ? await Filter() : Filter,
        Order: Order || { [meta.来源.replace(refer_name + REF_SEPARATOR, '')]: 'asc' },
        PageSize: 0,
      });
    };

    async function onLoadData() {
      const { Rows } = await (onLoad || onLoad_default)();
      const options = Rows.map(r => ({
        value: r.ID,
        label: r[meta.来源.replace(refer_name + REF_SEPARATOR, '')],
        title: Object.values(r).join(', '),
      }));
      dispatch({ rows: Rows, options });
    }

    async function onOpenChange(open) {
      if (open && !selectProps.options) await onLoadData();
      dispatch({ open });
    }

    function onSelectChange(value) {
      if (selectProps.options) {
        if (onChange) onChange(value);
      } else {
        const record = state.rows.find(r => r.ID === value) || {};
        const values = {};
        // 更新同组内其他引用字段的值
        loadModel.forEach(k => values[refer_name + REF_SEPARATOR + k] = record[k]);
        // 更新引用的ID值
        values[refer_name] = record.ID;
        // 回填表单
        form.setFieldsValue(values);
        if (onChange) onChange(values[meta.来源], record, form);
      }
    }

    return <Select options={state.options} open={state.open} onOpenChange={onOpenChange} onChange={onSelectChange} {...selectProps} />
  }

  function MetaPopup({ meta, value, onChange, Filter, Order, Parent, placeholder, inputProps, ...tableProps }) {
    const [modal, modalHolder] = useModal();
    const form = Form.useFormInstance();
    const is_in_filter = form === filter_form;

    const refer_name = meta.来源.split(REF_SEPARATOR).shift();
    const refer_fields = is_in_filter ? [...Querys, ...Forms, ...Tables].filter(q => q.来源.startsWith(refer_name + REF_SEPARATOR))
      : Forms.filter(f => f.来源.startsWith(refer_name + REF_SEPARATOR));

    const loadModel = Array.from(new Set(refer_fields.map(f => f.来源.replace(refer_name + REF_SEPARATOR, ''))));
    if (Parent) loadModel.push(Parent);

    const names = new Set(); // 去重
    const columns = refer_fields.map(f => {
      if (!f.类型 || names.has(f.名称)) return null;
      names.add(f.名称);
      const source = f.来源.replace(refer_name + REF_SEPARATOR, '');
      return {
        title: f.名称,
        dataIndex: source,
        width: 150,
        ellipsis: true,
        sorter: true,
        render(text, record, index) {
          if (text == null) return text;
          switch (f.类型) {
            case 'SZ': return Number(text).toFixed(f.精度 || 0);
            case 'RQ': return dayjs(text).format('YYYY-MM-DD');
            case 'SJ': return dayjs(text).format('YYYY-MM-DD HH:mm:ss');
            case 'BJ': return <Checkbox disabled checked={text} />;
            default: return text;
          }
        },
      };
    }).filter(f => f);

    async function onLoad_default(queryValue, queryOrder, pageNo, pageSize) {
      const order = Order || {};
      (Array.isArray(queryOrder) ? queryOrder : [queryOrder]).forEach(s => order[s.field] = s.order);

      Filter = typeof Filter === 'function' ? await Filter() : Filter ?? [];
      if (Parent && Filter.length === 0) Filter.push([Parent, 'is', null]);

      return await client.job(Parent ? 'TreeR' : 'CardR', {
        Card: meta.目标卡片,
        Model: loadModel,
        Filter: [[meta.来源.replace(refer_name + REF_SEPARATOR, ''), 'like', queryValue], ...Filter],
        Order: order,
        PageSize: pageSize,
        PageNo: pageNo,
        Parent,
      });
    }

    function onClick() {
      let selected = undefined;
      const handle = modal.open({
        title: '请选择记录',
        width: '80%',
        content: <SearchTable columns={columns} onLoad={onLoad_default} {...tableProps}
          onSelect={record => selected = record} placeholder={'搜索' + meta.名称}
          onSelectEnd={record => {
            handle.destroy();
            onTableSelect(record);
          }}
        />,
        onOk() {
          if (!selected) return false;
          onTableSelect(selected);
        }
      });
    }

    function onTableSelect(record = {}) {
      const values = {};
      // 更新同组内其他引用字段的值
      loadModel.forEach(k => values[refer_name + REF_SEPARATOR + k] = record[k]);
      // 更新引用的ID值
      values[refer_name] = record.ID;
      // 回填表单
      form.setFieldsValue(values);
      if (onChange) onChange(values[meta.来源], record, form);
    }

    return (
      <>
        <Input allowClear addonAfter={<SelectOutlined />} onClick={onClick} title={placeholder} placeholder={placeholder}
          value={value} onChange={e => e.target.value === '' ? onTableSelect() : null} {...inputProps} />
        {modalHolder}
      </>
    );
  }

  return (
    <Layout className={styles.mpcard} ref={root}>
      <Layout.Header className={styles.toolbar}>
        <Form form={filter_form} disabled={Reference && !mainSelected}
          onFinish={values => dispatch({ type: 'change', filters: values })}>
          {visibleQueryComponents}
          <Space.Compact>
            {withWorkflow ? (
              <Dropdown.Button disabled={Reference && !mainSelected} icon={<DownOutlined />}
                onClick={() => filter_form.submit()}
                menu={{
                  onClick: e => dispatch({ channel: e.key }),
                  items: [state.channel === '待办' ? {
                    key: '已办',
                    label: '查询已办记录',
                  } : {
                    key: '待办',
                    label: '查询待办记录',
                  }],
                }} >
                查询{state.channel}
              </Dropdown.Button>
            ) : (
              <Button htmlType="submit" disabled={Reference && !mainSelected}>{state.channel}</Button>
            )}
            <Button disabled={Reference && !mainSelected} onClick={onAdvanceQuery} title="高级查询" icon={<Iconfont name="searchlist" />}></Button>
          </Space.Compact>
        </Form>
        <Space wrap>
          {buttons.filter(b => b.位置 === 'toolbar').filter(btn => btn.visible.call(MPP, state.selected, state.checkedRows)).map(btn => (
            <Button key={btn.ID} type={btn.类型} danger={btn.危险} disabled={!btn.available.call(MPP, state.selected, state.checkedRows)}
              onClick={() => onButtonClick(btn.名称, { record: state.selected, checkedRows: state.checkedRows })}>{btn.名称}</Button>
          ))}
        </Space>
      </Layout.Header>
      <Layout.Content>
        <SelectTable loading={state.loading} pagination={state.pagination} showSorterTooltip={false}
          columns={tableColumns} dataSource={state.data} summary={() => tableSummary} keepCheck keepSelect
          onChange={(pagination, filters, sorter) => dispatch({ type: 'change', pagination, filters, sorter })}
          selectedRowKey={state.selected?.ID}
          rowSelection={{
            selectedRowKeys: state.checkedRows.map(r => r.ID),
            onSelect: (record, selected, selectedRows) => dispatch({ type: 'check', rows: selectedRows }),
            onSelectAll: (selected, selectedRows) => dispatch({ type: 'check', rows: selectedRows }),
            onSelectNone: () => dispatch({ type: 'check', rows: [] }),
          }}
          onRow={(record) => ({ onClick: () => dispatch({ type: 'select', record }) })}
        />
      </Layout.Content>
      {drawerHolder}
      {modalHolder}
    </Layout>
  );
}

function QueryCheckbox({ checked, onChange, ...props }) {
  const [value, setValue] = useState(checked);

  // 确保 value 状态与 checked 属性同步
  useEffect(() => {
    setValue(checked);
  }, [checked]);

  // 处理 onChange 事件
  const handleChange = useCallback((event) => {
    if (value === false && event.target.checked) { // false -> null
      setValue(null);
      onChange(null);
    } else {
      setValue(event.target.checked);
      onChange(event);
    }
  }, [value, onChange]);

  return (
    <Checkbox
      indeterminate={value == null}
      checked={value}
      onChange={handleChange}
      {...props}
    />
  );
}